package com.jingge.autojob.util.http;

import com.google.gson.JsonObject;
import com.google.gson.reflect.TypeToken;
import com.jingge.autojob.util.convert.StringUtils;
import com.jingge.autojob.util.json.JsonUtil;
import okhttp3.*;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Type;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 基于okHttp的HTTP客户端工具类
 *
 * @author JingGe(* ^ ▽ ^ *)
 * @date 2023-09-04 13:50
 * @email 1158055613@qq.com
 */
public class HttpUtil {
    private static final String JSON_TYPE = "application/json;charset=UTF-8";
    private static final String USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.139 Safari/537.36";

    //这些是HttpClient的配置，具体可以根据实际情况修改
    private static final Dispatcher dispatcher = new Dispatcher();

    static {
        dispatcher.setMaxRequests(100);
        dispatcher.setMaxRequestsPerHost(10);
    }

    public static OkHttpClient CLIENT = new OkHttpClient.Builder()
            .connectionPool(new ConnectionPool(10, 5, TimeUnit.MINUTES))
            .dispatcher(dispatcher)
            .connectTimeout(60, TimeUnit.SECONDS)
            .writeTimeout(30, TimeUnit.SECONDS)
            .readTimeout(30, TimeUnit.SECONDS)
            .build();

    /**
     * 修改OKHTTP客户端，工具类已提供默认实现
     *
     * @param client 要修改的客户端
     * @return void
     * @author JingGe(* ^ ▽ ^ *)
     * @date 2023/9/4 16:25
     */
    public static void setClient(OkHttpClient client) {
        if (client == null) {
            return;
        }
        HttpUtil.CLIENT = client;
    }

    public static HeaderBuilder headerBuilder() {
        return new HeaderBuilder();
    }

    public static HttpUrl.Builder urlBuilder() {
        return new HttpUrl.Builder();
    }

    public static Request.Builder requestBuilder() {
        return new Request.Builder();
    }

    public static HttpUrl.Builder urlBuilder(String url, Map<String, Object> queryParams) {
        if (StringUtils.isEmpty(url)) {
            throw new IllegalArgumentException();
        }
        HttpUrl.Builder builder = Objects
                .requireNonNull(HttpUrl.parse(url))
                .newBuilder();
        if (queryParams != null) {
            for (Map.Entry<String, Object> entry : queryParams.entrySet()) {
                builder.addQueryParameter(entry.getKey(), entry.getValue() instanceof String ? (String) entry.getValue() : entry
                        .getValue()
                        .toString());
            }
        }
        return builder;
    }

    public static HttpUrl.Builder urlBuilder(Map<String, Object> queryParams) {
        HttpUrl.Builder builder = new HttpUrl.Builder();
        if (queryParams != null) {
            for (Map.Entry<String, Object> entry : queryParams.entrySet()) {
                builder.addQueryParameter(entry.getKey(), entry.getValue() instanceof String ? (String) entry.getValue() : entry
                        .getValue()
                        .toString());
            }
        }
        return builder;
    }

    public static Response doRequestSync(Request request, OkHttpClient client) {
        if (request == null || client == null) {
            return null;
        }
        Call call = client.newCall(request);
        try {
            return call.execute();
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }

    public static Response doRequestSync(Request request) {
        return doRequestSync(request, CLIENT);
    }

    public static void doRequestAsync(Request request, Callback callback, OkHttpClient client) {
        Call call = client.newCall(request);
        call.enqueue(callback);
    }

    public static void doRequestAsync(Request request, Callback callback) {
        doRequestAsync(request, callback, CLIENT);
    }


    public static Response getSync(String url, Map<String, Object> params) {
        Request request = requestBuilder()
                .url(urlBuilder(url, params).build())
                .build();
        return doRequestSync(request);
    }

    public static void getAsync(String url, Map<String, Object> params, Callback callback) {
        Request request = requestBuilder()
                .url(urlBuilder(url, params).build())
                .build();
        doRequestAsync(request, callback);
    }

    public static Response postWithJsonBodySync(String url, Map<String, Object> params, Map<String, List<String>> header, Object body) {
        Request.Builder builder = requestBuilder().url(urlBuilder(url, params).build());
        if (header != null) {
            for (Map.Entry<String, List<String>> entry : header.entrySet()) {
                if (entry.getValue() == null) {
                    continue;
                }
                for (String value : entry.getValue()) {
                    builder.addHeader(entry.getKey(), value);
                }
            }
        }
        MediaType mediaType = MediaType.parse(JSON_TYPE);
        RequestBody requestBody = RequestBody.create(body instanceof String ? (String) body : JsonUtil.pojoToJsonString(body), mediaType);
        builder.post(requestBody);
        return doRequestSync(builder.build());
    }

    public static void postWithJsonBodyASync(String url, Map<String, Object> params, Map<String, List<String>> header, Object body, Callback callback) {
        Request.Builder builder = requestBuilder().url(urlBuilder(url, params).build());
        if (header != null) {
            for (Map.Entry<String, List<String>> entry : header.entrySet()) {
                if (entry.getValue() == null) {
                    continue;
                }
                for (String value : entry.getValue()) {
                    builder.addHeader(entry.getKey(), value);
                }
            }
        }
        MediaType mediaType = MediaType.parse(JSON_TYPE);
        RequestBody requestBody = RequestBody.create(body instanceof String ? (String) body : JsonUtil.pojoToJsonString(body), mediaType);
        builder.post(requestBody);
        doRequestAsync(builder.build(), callback);
    }

    public static boolean isJsonResponseSuccess(Response response, String verifyKey, int verifyCode) {
        if (response == null || response.code() != 200) {
            return false;
        }
        try {
            JsonObject jsonObject = JsonUtil.stringToJsonObj(Objects
                    .requireNonNull(response.body())
                    .string());
            return jsonObject != null && jsonObject.has(verifyKey) && jsonObject
                    .get(verifyKey)
                    .getAsInt() == verifyCode;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    public static String getResponseAsString(Response response) {
        if (response == null) {
            return null;
        }
        try {
            return Objects
                    .requireNonNull(response.body())
                    .string();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static JsonObject getResponseAsJsonObject(Response response) {
        if (!isJsonResponseSuccess(response, "code", 200)) {
            return null;
        }
        try {
            return JsonUtil.stringToJsonObj(Objects
                    .requireNonNull(response.body())
                    .string());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    public static <T> T getResponseAsInstance(Response response, Class<?> type) {
        if (!isJsonResponseSuccess(response, "code", 200)) {
            return null;
        }
        try {
            return (T) JsonUtil.jsonStringToPojo(Objects
                    .requireNonNull(response.body())
                    .string(), type);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static InputStream getResponseAsInputStream(Response response) {
        if (response == null || response.body() == null) {
            return null;
        }
        return Objects
                .requireNonNull(response.body())
                .byteStream();
    }

    public static class HeaderBuilder {
        private final Map<String, List<String>> headers = new HashMap<>();

        public HeaderBuilder addHeader(String name, String value) {
            if (!headers.containsKey(name)) {
                headers.put(name, new ArrayList<>());
            }
            headers
                    .get(name)
                    .add(value);
            return this;
        }

        public Map<String, List<String>> getHeaders() {
            return headers;
        }
    }

    @SuppressWarnings("unchecked")
    public static Map<String, Object> getQueryParams(Object params) {
        if (params == null) {
            return null;
        }
        if (params instanceof Map) {
            return (Map<String, Object>) params;
        }
        if (params instanceof String) {
            try {
                return JsonUtil.jsonStringToMap((String) params, String.class, Object.class);
            } catch (Exception ignored) {
            }
        }
        return null;
    }

    @SuppressWarnings("unchecked")
    public static Map<String, List<String>> getHeaders(Object headers) {
        if (headers == null) {
            return null;
        }
        if (headers instanceof Map) {
            return (Map<String, List<String>>) headers;
        }
        if (headers instanceof String) {
            try {
                Type mapType = new TypeToken<Map<String, List<String>>>() {
                }.getType();
                return JsonUtil.jsonString2Instance((String) headers, mapType);
            } catch (Exception ignored) {
            }
        }
        return null;
    }

    public static void configHeader(Request.Builder builder, Map<String, List<String>> headers) {
        if (headers == null || builder == null) {
            return;
        }
        for (Map.Entry<String, List<String>> entry : headers.entrySet()) {
            if (entry.getValue() == null) {
                continue;
            }
            for (String value : entry.getValue()) {
                builder.addHeader(entry.getKey(), value);
            }
        }
        if (!headers.containsKey("User-Agent")) {
            builder.addHeader("User-Agent", USER_AGENT);
        }
        if (!headers.containsKey("Connection")) {
            builder.addHeader("Connection", "keep-alive");
        }
        if (!headers.containsKey("Content-Type")) {
            builder.addHeader("Content-Type", JSON_TYPE);
        }
    }

    public static Map<String, List<String>> defaultHeader() {
        return headerBuilder()
                .addHeader("Connection", "keep-alive")
                .addHeader("Content-Type", JSON_TYPE)
                .addHeader("User-Agent", USER_AGENT)
                .getHeaders();
    }


}
